﻿using System.Collections.Generic;
using System.IO;

using Storyteller.Framework.Graphics;
using System;

namespace Storyteller.Framework.Assets
{
	public class AssetManager : IDisposable
	{
		public string AssetPath { get; private set; }

		public AssetManager(string assetPath)
		{
			if (string.IsNullOrEmpty(assetPath))
				throw new ArgumentNullException("AssetManager assetPath is invalid");

			string assemblyPath = new Uri(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath;
			AssetPath = Path.Combine(Path.GetDirectoryName(assemblyPath), assetPath);

			string lastCharacter = assetPath.Substring(assetPath.Length - 1);
			if (lastCharacter != "/")
				AssetPath += "/";

			contentManager = new Microsoft.Xna.Framework.Content.ContentManager(Game.Instance.Services, assetPath);
		}

		public T Load<T>(string filePath) where T : class
		{
			object asset;

			if (assets.TryGetValue(filePath, out asset))
			{
				return asset as T;
			}

			string assetName = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath));

			if (typeof(T) == typeof(Texture))
			{
				object t = (object)new Texture(contentManager.Load<Microsoft.Xna.Framework.Graphics.Texture2D>(assetName));
				assets[filePath] = t;
				return (T)t;
			}
			if (typeof(T) == typeof(SpriteShader))
			{
				object t = (object)new SpriteShader(contentManager.Load<Microsoft.Xna.Framework.Graphics.Effect>(assetName));
				assets[filePath] = t;
				return (T)t;
			}
			else
			{
				T t = Activator.CreateInstance(typeof(T), new object[] { Path.Combine(AssetPath, filePath) }) as T;
				assets[filePath] = t as object;

				if (t is IDisposable)
					disposableAssets.Add(t as IDisposable);

				return t;
			}
		}

		public T Load<T>(string assetName, params object[] parameters) where T : class
		{
			object asset;
			if (assets.TryGetValue(assetName, out asset))
			{
				return asset as T;
			}

			T t = Activator.CreateInstance(typeof(T), parameters) as T;
			assets[assetName] = t as object;

			if (t is IDisposable)
				disposableAssets.Add(t as IDisposable);

			return t;
		}

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (!disposed)
			{
				if (disposing)
				{
					foreach (var v in disposableAssets)
					{
						v.Dispose();
					}

					contentManager.Dispose();
					disposableAssets.Clear();
					assets.Clear();
				}

				// dispose of unmanaged resources
				disposed = true;
			}
		}

		public string GetPath(string filePath)
		{
			return Path.Combine(AssetPath, filePath);
		}

		Dictionary<string, object> assets = new Dictionary<string,object>();
		Microsoft.Xna.Framework.Content.ContentManager contentManager;

		List<IDisposable> disposableAssets = new List<IDisposable>();
		bool disposed = false;
	}
}
